package dohmh.yelp.analysis;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

import javax.xml.stream.XMLStreamException;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.lang.time.DateUtils;
import org.supercsv.io.CsvListWriter;
import org.supercsv.prefs.CsvPreference;

import dohmh.yelp.classifiers.WekaClassifier;
import dohmh.yelp.dataobjects.Business;
import dohmh.yelp.dataobjects.Classification;
import dohmh.yelp.dataobjects.Review;
import dohmh.yelp.exceptions.DOHMHException;
import dohmh.yelp.utilities.DataClean;
import dohmh.yelp.utilities.FeedDownload;
import dohmh.yelp.utilities.XMLFeedParser;

public class ReviewAnalysis {

	public static void main(String[] args) {
		BufferedReader in = null;
		in = new BufferedReader(new InputStreamReader(System.in));
		String _input = "";
		printInstructions();
		try {
			while (!_input.toLowerCase().equals("3")){
				_input = readLine(in);
				// Download a new feed
				if (_input.toLowerCase().equals("1")){
					FeedDownload _feedDownload = new FeedDownload();
					String download = "y";
					Boolean haveLatest = _feedDownload.haveLatest();
					if (haveLatest){
						System.out.println("The latest feed file is already downloaded. Download anyway? (y/n)");
						download = readLine(in);
					}
					if (!haveLatest || (download.toLowerCase().equals("y") || download.toLowerCase().equals("yes"))){
						_feedDownload.begin();
						for (String fFile : _feedDownload.getFeedFile()){
							DataClean.addCDATA(fFile);	
						}
					}
					else {
						System.out.println("Download canceled.");
					}
				}
				else if (_input.toLowerCase().equals("2")){
					System.out.println("Enter start date of date range (MM/DD/YY):");
					Date startDate = validateDate(in);
					System.out.println("Enter end date of date range (MM/DD/YY):");
					Date endDate = validateDate(in);
					System.out.println("Enter confidence threshold:");
					Double thresh = validateDouble(in);
					
					analyzeReviews(startDate, endDate, thresh);
				}
				else if (_input.toLowerCase().equals("3")){
					System.exit(0);
				}
				else {
					System.out.println("Invalid input. Please try again");
				}
				printInstructions();
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ConfigurationException e) {
			e.printStackTrace();
		} catch (DOHMHException e) {
			e.printStackTrace();
		}
	}

	private static void printInstructions(){
		StringBuilder sb = new StringBuilder();
		sb.append("Yelp Review Analysis\n");
		sb.append("------------------------------\n");
		sb.append("1.) Download a new feed.\n");
		sb.append("2.) Analyze reviews.\n");
		sb.append("3.) Exit\n");
		sb.append("Type \"exit\" to exit anytime\n");
		sb.append("------------------------------");
		System.out.println(sb.toString());
	}
	
	private static Date validateDate(BufferedReader in) throws IOException{
		SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yy");
		Boolean validDate = false;
		Date returnDate = null;
		while (!validDate){
			try {
				String date = readLine(in);
				returnDate = sdf.parse(date);
				
				validDate = true;
			} catch (ParseException e) {
				System.out.println("Invalid date format. Please enter date as MM/DD/YY");
			}
		}
		return returnDate;
	}
	
	private static Double validateDouble(BufferedReader in) throws IOException{
		Boolean validDouble = false;
		Double returnDouble = null;
		while (!validDouble){
			try {
				returnDouble = Double.parseDouble(readLine(in));
				if (returnDouble > 1 || returnDouble < 0){
					throw new NumberFormatException();
				}
				validDouble = true;
			} catch (NumberFormatException e){
				System.out.println("Invalid threshold value. Please enter a decimal value between 0 and 1");
			} 
		}
		return returnDouble;
	}
	
	private static String readLine(BufferedReader in) throws IOException{
		String returnString = in.readLine();
		if (returnString.toLowerCase().equals("exit")){
			System.exit(0);
		}
		return returnString;
	}

	private static void analyzeReviews(Date start, Date end, Double thresh) throws ConfigurationException, FileNotFoundException, IOException, DOHMHException{
		Configuration systemConfig, downloadConfig;
		systemConfig = new PropertiesConfiguration("system.properties");
		downloadConfig = new PropertiesConfiguration("download.properties");
		String[] _feedPath = downloadConfig.getStringArray("FEED_PATH");
		String _encoding = downloadConfig.getString("FEED_ENCODING");
		String [] _classifierPaths = systemConfig.getStringArray("WEKA_MODELS");
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HHmmss");
		String _analysisPath = systemConfig.getString("ANALYSIS_PATH") + "yelp_analysis-" + sdf.format(new Date()) + ".csv";
		WekaClassifier [] _classifiers = new WekaClassifier [_classifierPaths.length];
		String [] _classValues = systemConfig.getStringArray("CLASS_VALUES");
		ArrayList<String> _data = new ArrayList<String>();
		int[] secondaryClassifiers = new int[_classifierPaths.length];
		
		CsvListWriter _resultsWriter = new CsvListWriter(new FileWriter(_analysisPath), CsvPreference.EXCEL_PREFERENCE);
		_data.add("Date");
		_data.add("URL");
		_data.add("Business");
		_data.add("Review");
		for (int i = 0; i < _classifierPaths.length; i++){
			if (_classifierPaths[i].endsWith(":sec")){
				secondaryClassifiers[i] = 1;
				_classifierPaths[i] = _classifierPaths[i].replace(":sec", "");
			}
			else {
				secondaryClassifiers[i] = 0;
			}
			_classifiers[i] = new WekaClassifier(_classifierPaths[i], _classValues);
			_data.add(_classifierPaths[i].substring(_classifierPaths[i].lastIndexOf("/") + 1, _classifierPaths[i].lastIndexOf(".")));
		}
		_data.add("Score");
		_resultsWriter.write(_data);
		_data.clear();
		Evaluation _eval = new Evaluation(_classifiers);
		try {
			sdf.applyPattern("MM/dd/yyyy");
			System.out.print("Analyzing reviews for range " + sdf.format(start) + "-" + sdf.format(end) + " ...");
			for (String fPath : _feedPath){
				XMLFeedParser parser = new XMLFeedParser(fPath, _encoding);
				while (parser.hasBusinesses()){
					Business _business = parser.getBusiness();
					for (Review _rev : _business.getReviews()){
						if (_rev.getTimeCreated().after(DateUtils.addDays(start, -1)) && _rev.getTimeCreated().before(DateUtils.addDays(end, 1))){
							_data.add(sdf.format(_rev.getTimeCreated()));
							_data.add(_rev.getUrl());
							_data.add(_business.getName());
							_data.add(_rev.getText());
							Classification [] c = _eval.classify(_rev);
							double score = 0.0;
							int norm = 0;
							for (int i = 0; i < c.length; i++){
								_data.add(String.format("%.10f", c[i].getConfidenceScores()[1]));
								if (secondaryClassifiers[i] != 1){
									score += c[i].getConfidenceScores()[1];
									norm++;
								}
							}
							double maxVal = 0.0;
							for (int i = 0; i < secondaryClassifiers.length; i++){
								if (secondaryClassifiers[i] == 1 && c[i].getConfidenceScores()[1] > maxVal){
									maxVal = c[i].getConfidenceScores()[1];
								}
							}
							_data.add(String.format("%.10f", (score + maxVal) / ((double) norm + 1)));
							if ((score + maxVal) / ((double) norm + 1) > thresh){
								_resultsWriter.write(_data);	
							}
							_data.clear();
						}	
					}
				}
			}
			System.out.println("Done");
			File f = new File(_analysisPath);
			System.out.println("Results written out to " + f.getAbsolutePath());
		} catch (XMLStreamException e) {
			System.out.println("There is a problem reading the xml file. Please try downloading a new file or fixing the file that is already downloaded.");
			//printInstructions();
			e.printStackTrace();
		} finally {
			_resultsWriter.close();
		}
	}
}
